import { Multinomial } from "./Inference/Utils/Multinomial";
import { Random, RandomNormal } from "./Inference/Utils/RandomNormal";


export class SamplerFactory {
    private static _random: Random;

    public static createUniformSampler(min: number, max: number, seed: number): () => number {
        return () => min + new Random(seed).nextFloat() * (max - min);
    }

    public static createGaussianSampler(mean: number, stddev: number, seed: number): () => number {
        const randomNormal = new RandomNormal(seed, mean, stddev);
        return () => randomNormal.nextFloat();
    }

    public static createMultiRangeUniformSampler(intervals: number[], seed: number): () => number {
        if (!SamplerFactory._random) {
            SamplerFactory._random = new Random(seed);
        }
        const numIntervals = intervals.length / 2;
        const intervalSizes: number[] = [];
        const intervalFuncs: (() => number)[] = [];
        let sumIntervalSizes = 0;

        for (let i = 0; i < numIntervals; i++) {
            const min = intervals[2 * i];
            const max = intervals[2 * i + 1];
            const intervalSize = max - min;
            sumIntervalSizes += intervalSize;
            intervalSizes[i] = intervalSize;
            intervalFuncs[i] = () => min + SamplerFactory._random.nextFloat() * intervalSize;
        }

        for (let i = 0; i < numIntervals; i++) {
            intervalSizes[i] = intervalSizes[i] / sumIntervalSizes;
        }

        for (let i = 1; i < numIntervals; i++) {
            intervalSizes[i] += intervalSizes[i - 1];
        }

        const intervalDistr = new Multinomial(seed + 1);

        function multiRange(): number {
            const sampledInterval = intervalDistr.sample(intervalSizes);
            return intervalFuncs[sampledInterval]();
        }

        return multiRange;
    }
}